package com.alex.taskmanager.service.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alex.taskmanager.dao.TaskData;
import com.alex.taskmanager.dao.TaskManagerDao;
import com.alex.taskmanager.dao.TaskStatus;
import com.alex.taskmanager.service.TaskExecutionException;
import com.alex.taskmanager.service.TaskExecutor;
import com.alex.taskmanager.service.TaskManager;
import com.alex.taskmanager.service.TaskManagerException;
import com.alex.taskmanager.service.TaskRunnerFactory;

public class DefaultTaskManager implements TaskManager, TaskRunnerFactory {

	private static final Logger LOGGER = LoggerFactory.getLogger(DefaultTaskManager.class);
	
	private TaskManagerDao taskManagerDao;

	public DefaultTaskManager() {
	}

	public DefaultTaskManager(TaskManagerDao taskManagerDao) {
		this.taskManagerDao = taskManagerDao;
	}

	public void setTaskManagerDao(TaskManagerDao taskManagerDao) {
		this.taskManagerDao = taskManagerDao;
	}

	public <T> void addTask(String name, TaskExecutor<T> taskExecutor, T params, long time, int maxRetries) throws TaskManagerException {
		taskManagerDao.storeTask(new TaskData<T>(name, TaskStatus.PENDING, (int) (time / 1000), taskExecutor, params, maxRetries));
	}

	public <ParamType> Runnable getTaskRunner(final TaskData<ParamType> taskData) {
		return new Runnable() {
			public void run() {
				try {
					if (taskData.getRetryNumber() == 0) {
						LOGGER.info("Executing task [" + taskData.getId() + "] \"" + taskData.getName() + "\".");
					} else {
						LOGGER.info("Retry " + taskData.getRetryNumber() + " executing task [" + taskData.getId() + "] \"" + taskData.getName() + "\".");
					}
					taskData.getExecutor().execute(taskData);
					taskData.setStatus(TaskStatus.COMPLETED);
					LOGGER.info("Completed task [" + taskData.getId() + "] \"" + taskData.getName() + "\"");
				} catch (TaskExecutionException e) {
					LOGGER.info("Error executing task [" + taskData.getId() + "] \"" + taskData.getName() + "\": " + e.getMessage(), e);
					if (e.isRetry() && taskData.getRetryNumber() < taskData.getMaxRetries()) {
						taskData.setStatus(TaskStatus.PENDING);
						taskData.incRetryNumber();
					} else {
						LOGGER.info("Max retry count " + taskData.getMaxRetries() + " exceeded. Task [" + taskData.getId() + "] \"" + taskData.getName() + "\" failed.");
						taskData.setStatus(TaskStatus.FAILED);
					}
				} catch (Exception e) {
					LOGGER.info("Error executing task [" + taskData.getId() + "] \"" + taskData.getName() + "\": " + e.getMessage(), e);
					taskData.setStatus(TaskStatus.FAILED);
				} finally {
					try {
						if (taskData.getStatus() == TaskStatus.COMPLETED) {
							taskManagerDao.deleteTask(taskData);
						} else {
							taskManagerDao.updateTask(taskData);
						}
					} catch (TaskManagerException e) {
						LOGGER.error("Failed to update task: " + e.getMessage(), e);
					}
				}
			}
		};
	}

}
